package spring.mvc;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import spring.mvc.interceptor.SimpleHandlerInterceptor;

/**
 * mvc 配置类，相当于 dispatcher-servlet.xml
 *
 * SpringMVC 没有自动配置类哦，所以需要使用 @EnableWebMvc 手动启用 spring 的 mvc默认配置
 *
 * @author ME
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "spring.mvc")
public class MvcConfig implements WebMvcConfigurer {
// public class MvcConfig extends WebMvcConfigurerAdapter {

    /**
     * 配置视图解析器，用来映射到实际视图对象的机制
      */
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }

    /**
     * 通过 ResourceHttpRequestHandler 处理 `静态资源` 的HTTP请求，包括图片、JavaScript、CSS 文件等，最终存放导 SimpleUrlHandlerMapping.urlMap
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // registry.addResourceHandler("/images/**").addResourceLocations("classpath:/static/images/");
        WebMvcConfigurer.super.addResourceHandlers(registry);
    }

    /**
     * 把 mjs 资源以 `application/javascript` 格式返回
     * @param configurer
     */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.mediaType("mjs", org.springframework.http.MediaType.valueOf("application/javascript"));
    }

    /**
     * 通过 ParameterizableViewController 处理请求，用于为某个 URL 路径直接返回一个视图而不需要后端逻辑处理，最终存放导 SimpleUrlHandlerMapping.urlMap
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
        WebMvcConfigurer.super.addViewControllers(registry);
    }

    /**
     * 注册拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SimpleHandlerInterceptor()).addPathPatterns("/**");
    }

    /**
     * 配置静态资源处理
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        // 将静态资源的请求转发到servlet容器中默认的servlet上
        configurer.enable();
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
    }

    @Bean
    public ObjectMapper objectMapper() {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        // MVC 需要显式注册，JavaTimeModule 天然支持 ZonedDateTime
        builder.modulesToInstall(new JavaTimeModule(), new Jdk8Module(), new ParameterNamesModule());
        // JavaTimeModule 默认的模板为 `2023-04-01T15:00:00`，再手动覆盖
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(formatter))
                .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));

        return builder.build();
    }

    // @Override
    // public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    //     // 设置响应头信息
    //     List<MediaType> list = new ArrayList<>();
    //     // list.add(MediaType.TEXT_PLAIN);// 纯文本格式
    //     // list.add(MediaType.TEXT_HTML);// html格式
    //     list.add(MediaType.APPLICATION_JSON_UTF8);
    //     // list.add(MediaType.ALL);
    //
    //     // 设置一个自定义的 消息转换器
    //     Charset charset = Charset.forName("UTF-8");
    //     StringHttpMessageConverter converter = new StringHttpMessageConverter(charset);
    //     converter.setSupportedMediaTypes(list);
    //
    //     // 设置 MessageConverter
    //     // converters.add(converter);
    //     converters.add(new MappingJackson2HttpMessageConverter());
    // }

    /**
     * todo
     *  1，实现 `getValidator()` 和 使用`@Bean` 配置 Validator，都可以应用于 mvc。
     *  2，getValidator() 配置仅在 Web 层进行参数验证的验证器。而使用 @Bean 声明验证器允许在整个应用中重用。
     */
    @Override
    public Validator getValidator() {
        return new LocalValidatorFactoryBean();
    }

    // @Bean
    // public javax.validation.Validator validator() {
    //     return new LocalValidatorFactoryBean();
    // }

}
